﻿using System;
using System.Linq;
using System.Text;
using System.Threading;
using EP.Utilities;
using Microsoft.Practices.Unity.InterceptionExtension;
using VerifyArgs;
using System.Reflection;
using EP.Caching.Attributes;

namespace EP.Caching.KeyGen {
	public class ArgumentHashCodeCacheKeyGenerator : ICacheKeyGenerator {
		public CacheScope Scope { get; set; }

		private readonly IHttpContextAccessor _httpContextAccessor;

		public ArgumentHashCodeCacheKeyGenerator(IHttpContextAccessor httpContextAccessor) {
			Verify.Args(new { httpContextAccessor }).NotNull();

			_httpContextAccessor = httpContextAccessor;
		}

		public string GetKey(MethodBase input, IParameterCollection arguments, object target) {
			Verify.Args(new { input }).NotNull();

			return GetMethodSignature(input) + GetScope() + GetArgumentsHashCode(input, arguments) + GetAppendKeys(input, target);
		}

		private string GetMethodSignature(MethodBase input) {
			Verify.Args(new { input }).NotNull();

			return input.Name;
		}
		private string GetScope() {
			switch (Scope) {
				case CacheScope.Default:
				case CacheScope.Global:
					break;

				case CacheScope.User:
					if (Thread.CurrentPrincipal != null && Thread.CurrentPrincipal.Identity != null && Thread.CurrentPrincipal.Identity.IsAuthenticated)
						return Thread.CurrentPrincipal.Identity.Name;
					break;

				case CacheScope.Session:
					var httpContext = _httpContextAccessor.Current();
					if (httpContext != null && httpContext.Session != null)
						return httpContext.Session.SessionID;
					break;
			}

			return string.Empty;
		}
		private string GetArgumentsHashCode(MethodBase input, IParameterCollection arguments) {
			Verify.Args(new { input, arguments }).NotNull();

			var key = new StringBuilder();

			for (var i = 0; i < arguments.Count; i++) {
				if (arguments[i] != null && !arguments.GetParameterInfo(i).GetCustomAttributes(typeof(IgnoreForCacheKeyAttribute), true).Any()) {
					key.Append("_");
					key.Append(arguments[i].GetHashCode());
				}
			}

			return key.ToString();
		}
		private string GetAppendKeys(MethodBase input, object target) {
			Verify.Args(new { input }).NotNull();

			var key = new StringBuilder();

			var appendKeys = input.GetCustomAttributes(typeof(AddToCacheKeyAttribute), true) as AddToCacheKeyAttribute[];
			if (appendKeys != null) {
				foreach (var appendKey in appendKeys.OrderBy(s => s.Order)) {
					key.Append(appendKey.GetKey(input, target));
				}
			}

			return key.ToString();
		}
	}
}
